#include "StdAfx.h"

SelectionMgr* Singleton<SelectionMgr>::m_instance = 0;
DBCStore<AreaTableEntry> dbcArea;
DBCStore<GroundEffectDoodad> dbcGroundEffect;
DBCStore<GroundEffectTexture> dbcGroundTexture;
DBCStore<MapEntry> dbcMap;
DBCStore<LoadingScreenEntry> dbcLoadingScreen;

float fogDistance = 1.0f * TILESIZE;

SelectionMgr::SelectionMgr() : m_coordinates("Fonts\\FrizQT__.ttf", "Friz Quadrata TT", 7, 15)
{
	std::string dbcError;
	if(!dbcLoadingScreen.Load("DBFilesClient\\LoadingScreens.dbc", "ussu", dbcError))
	{
		StartupException e;
		e.szBody = "Unable to load LoadingScreens.dbc:\n";
		e.szBody += dbcError;
		e.szTitle = "DBC error";
		throw e;
	}
	if(!dbcMap.Load("DBFilesClient\\Map.dbc", "usuuulxxxxxxxxxxxxxxxxulxxxxxxxxxxxxxxxxlxxxxxxxxxxxxxxxxuuuuuuuuu", dbcError))
	{
		StartupException e;
		e.szBody = "Unable to load Map.dbc:\n";
		e.szBody += dbcError;
		e.szTitle = "DBC error";
		throw e;
	}
	if(!dbcArea.Load("DBFilesClient\\AreaTable.dbc", "uuuuuuuuuuulxxxxxxxxxxxxxxxxuuuuuuuu", dbcError))
	{
		StartupException e;
		e.szBody = "Unable to load AreaTable.dbc:\n";
		e.szBody += dbcError;
		e.szTitle = "DBC error";
		throw e;
	}
	if(!dbcGroundEffect.Load("DBFilesClient\\GroundEffectDoodad.dbc", "usu", dbcError))
	{
		StartupException e;
		e.szBody = "Unable to load GroundEffectDoodad.dbc:\n";
		e.szBody += dbcError;
		e.szTitle = "DBC error";
		throw e;
	}
	if(!dbcGroundTexture.Load("DBFilesClient\\GroundEffectTexture.dbc", "uuuuuuuuuuu", dbcError))
	{
		StartupException e;
		e.szBody = "Unable to load GroundEffectTexture.dbc:\n";
		e.szBody += dbcError;
		e.szTitle = "DBC error";
		throw e;
	}

	m_texFlags = 0;
	new WdlCreator();
	m_lastAreaId = 0;
	m_radius = 9;
	m_intensity = 5;
	m_drawSel = false;
	m_type = 0;
	m_selCircle = CTextureHandler::GetInstance()->GetTexture("Interface\\SpellShadow\\Spell-Shadow-Acceptable.blp");
	//m_selCircle = CTextureHandler::GetInstance()->GetTexture("World\\Goober\\RUNERING.BLP");
	m_upBar = new ToolBar(0);
	m_upBar->AddText(512, "");
	m_upBar->AddText(1000, "");
	m_upBar->AddText(50, "");
	sUI.AddElement(m_upBar);
	m_toolBar = new ToolBar(sD3D.GetSize().y - 58, 58);
	sUI.AddElement(m_toolBar);
	
	m_toolBar->AddIconButton("Interface\\Icons\\Spell_Fire_BlueHellfire.blp", 20, "Terrain",
		"The terrain menu will show various information concerning modification of the terrain. You can select "\
		"the type of modification, how changes are interpolated inside the brush and various other things.\n\n"\
		"Important shortcuts:\n"\
		"Ctrl + Left click -> Raise terrain\n"\
		"Alt + Left click -> Lower terrain",
		[this](IconButton*) { ToggleTerrainUI(NULL); });

	m_toolBar->AddIconButton("Interface\\Icons\\Ability_Paladin_SacredCleansing.blp", 70, "Texturing",
		"[Description]",
		[this](IconButton*) { ToggleTextureUI(NULL); });

	m_toolBar->AddIconButton("Interface\\Icons\\Ability_Hunter_Pathfinding.blp", 120, "Reset Camera",
		"Resets the camera to the location you first entered the world during this session.",
		[this](IconButton*) { ResetCam(NULL); });

	m_toolBar->AddIconButton("Interface\\Icons\\Spell_Shaman_LavaFlow.blp", 170, "Create WDL",
		"Creates a file which contains low resolution height information for the whole continent."\
		" The client uses this information to draw the background when you are moving in the "\
		"world to make sure hills arent coming out of nowhere ;)",
		[this](IconButton*) { CreateWdl(NULL); });

	m_toolBar->AddIconButton("Interface\\Icons\\Spell_Nature_Slow.blp", 220, "DBC",
		"This menu allows you to modify several client related database files concerning terrain (DBC files). "\
		"The following ones are contained:\n"\
		"- AreaTable.dbc",
		[this](IconButton*) { ToggleAreaDbc(NULL); });

	m_toolBar->AddIconButton("Interface\\Icons\\Spell_Holy_InnerFire.blp", 270, "Textures",
		"This menu allows you to browse, cache and use all the common textures that can be used to draw the ground "\
		"of your terrain. It supports the textures available in the ADT the camera is hovering, the chunk the camera "\
		"is hovering or all the textures used in a certain zone (called tilesets).",
		[this](IconButton*) { ToggleTexSelUI(NULL); });

	m_toolBar->AddIconButton("Interface\\Icons\\Spell_Arcane_FocusedPower.blp", 320, "Settings",
		"Allows you to change and save various settings from the application.",
		[this](IconButton*) { ToggleSettings(NULL); });

	
	m_layerUI = new LayerUI(m_toolBar);
	m_terrainUI = new TerrainUI();
	m_terrainUI->Toggle();
	m_texUI = new TextureUI();
	m_texUI->Toggle();
	m_texSel = new TexSelUI(m_texUI);
	m_texSel->Toggle();
	new AreaDialog();
	sAreaDialog.Hide();
	new SettingsDialog();
	sSettings.Toggle();
	new CommandUI();
	m_drawWMO = m_drawMDX = true;
	//new ModelSelect();
	sCommUI.Toggle();
	m_quickMode = false;
	m_mode = 0;
	m_quickKey = false;
}

void SelectionMgr::ShowTextureSelection()
{
	m_texSel->Show();
}

void SelectionMgr::CheckKeys()
{
	if(InputBox::GetInstance() && sInput.HasFocus())
		return;
	if(sUI.GetEditFocus())
		return;

	BYTE pKeys[256];
	GetKeyboardState(pKeys);
	if(pKeys[0x52] & 0x80 && m_drawSel)
	{
		if(m_mode == 0)
		{
			sD3D.ChangeHeight(m_position, m_radius, m_intensity, m_type);
			m_position.y += sD3D.GetLandHeight(m_position.x, m_position.z);
		}
		else if(m_mode == 1)
		{
			sD3D.FlattenHeight(m_position, m_radius, powf(0.9f, m_intensity / 20), m_type);
			m_position.y = sD3D.GetLandHeight(m_position.x, m_position.z);
		}
		else if(m_mode == 2)
		{
			sD3D.BlurHeight(m_position, m_radius, m_intensity, m_type);
			m_position.y = sD3D.GetLandHeight(m_position.x, m_position.z);
		}
	}
	if(pKeys[0x4C] & 0x80 && m_drawSel)
	{
		if(m_mode == 0)
		{
			sD3D.ChangeHeight(m_position, m_radius, -m_intensity, m_type);
			m_position.y -= sD3D.GetLandHeight(m_position.x, m_position.z);
		}
		else if(m_mode == 1)
		{
			sD3D.FlattenHeight(m_position, m_radius, powf(0.9f, m_intensity / 20), m_type);
			m_position.y = sD3D.GetLandHeight(m_position.x, m_position.z);
		}
		else if(m_mode == 2)
		{
			sD3D.BlurHeight(m_position, m_radius, m_intensity, m_type);
			m_position.y = sD3D.GetLandHeight(m_position.x, m_position.z);
		}
	}
	if(pKeys[0x49] & 0x80 && (pKeys[0x10] & 0x80))
	{
		if((m_radius - 0.2) <= 1.0f)
		{
			m_radius = 1.0f;
			return;
		}
		m_radius -= 0.2f;
		m_terrainUI->SetRadius(m_radius);
	}
	if(pKeys[0x49] & 0x80 && !(pKeys[0x10] & 0x80))
	{
		if(m_radius >= 200 || m_radius + 0.2f >= 200)
		{
			m_radius = 200.0f;
			return;
		}
		m_radius += 0.2f;
		m_terrainUI->SetRadius(m_radius);
	}
	if(pKeys[0x56] & 0x80 && (pKeys[0x10] & 0x80))
	{
		if((m_intensity - 0.1f) <= 0)
			return;
		m_intensity -= 0.1f;
		m_terrainUI->SetIntensity(m_intensity);
	}
	if(pKeys[0x56] & 0x80 && !(pKeys[0x10] & 0x80))
	{
		if(m_intensity >= 20.0f || m_intensity + 0.1f > 20.0f)
			return;

		m_intensity += 0.1f;
		m_terrainUI->SetIntensity(m_intensity);
	}
}

void SelectionMgr::OnKey(char key)
{
	if(sD3D.GetAppState() != STATE_WORLD)
		return;

	if(InputBox::GetInstance() && sInput.HasFocus())
		return;

	if(sUI.GetEditFocus())
		return;

	switch(key)
	{
	case 't':
	case 'T':
		m_type = (m_type + 1) % 4;
		m_terrainUI->SetType(m_type);
		break;
	case 'p':
		{
			sD3D.Save();
			break;
		}
	case '\r':
		{
			if(InputBox::GetInstance())
				sInput.OnEnterPressed();
			break;
		}
	case '+':
		{
			fogDistance += 0.1f * TILESIZE;
			break;
		}
	case '-':
		{
			fogDistance -= 0.1f * TILESIZE;
			if(fogDistance < 0.1f * TILESIZE)
				fogDistance = 0.1f * TILESIZE;
			break;
		}
	case 'y':
		{
			sUI.Toggle();
			break;
		}
	case 'F':
	case 'f':
		{
			Vertex3F pos = sD3D.GetWorldCoords((ui32)sMouse.GetPosition().x, (ui32)sMouse.GetPosition().y);
			ui32 indexX = (ui32)floor(pos.x / TILESIZE);
			ui32 indexY = (ui32)floor(pos.z / TILESIZE);
			ADTModel* mod = sD3D.GetADT(indexX, indexY);
			if(!mod)
				break;
			float rx = pos.x - (indexX * TILESIZE);
			float ry = pos.z - (indexY * TILESIZE);
			indexX = (ui32)floor(rx / CHUNKSIZE);
			indexY = (ui32)floor(ry / CHUNKSIZE);
			ADTChunk* c = mod->GetChunk(indexX, indexY);
			c->ToggleLock();
			break;
		}
	case 'c':
		{
			POINT ptMouse;
			GetCursorPos(&ptMouse);
			WINDOWINFO inf;
			GetWindowInfo(sWindow, &inf);
			ptMouse.x -= inf.rcClient.left;
			ptMouse.y -= inf.rcClient.top;
			sD3D.ToggleHole(sD3D.GetWorldCoords(ptMouse.x, ptMouse.y));
			break;
		}
	}
}

void SelectionMgr::SetSelectionTexture(ID3DXEffect* pEffect)
{
	pEffect->SetFloat("radius", m_radius);
	D3DXMATRIX& matView = sCamera.GetMatView();
	D3DXMATRIX& matProj = sD3D.GetMatProj();
	D3DXMATRIX viewProj = matView * matProj;
	pEffect->SetMatrix("viewProj", &viewProj);
	pEffect->SetFloatArray("mouse", m_lastMouse, 3);
	pEffect->SetBool("drawSel", m_drawSel);
	pEffect->SetFloatArray("click", m_position, 3);
	pEffect->SetTexture("selTex", *m_selCircle);
	pEffect->SetFloat("brushType", (float)m_texUI->GetBrushType());
	pEffect->SetFloat("sqrt2", Math::sqrt2);
	pEffect->SetBool("texEnable", sCommUI.TextureEnabled());
	pEffect->SetBool("drawHL", sSettings.GetHLEnabled());
	pEffect->SetFloat("hlDist", sSettings.GetHLDistance());
}

void SelectionMgr::Render()
{
	if(sMouse.LeftDown() && !sUI.IsUIHold() /*&& !sModelSel.GetModelSelected()*/)
	{
		BYTE state[256];
		GetKeyboardState(state);
		if(state[VK_CONTROL] & 0x80)
		{
			m_quickKey = true;
			if(sCommUI.TextureEnabled())
			{
				sD3D.ChangeTexture(sD3D.GetWorldCoords((ui32)sMouse.GetPosition().x, (ui32)sMouse.GetPosition().y), 
					m_radius, m_intensity, m_texFlags, m_texUI->GetTexture(), m_texUI->GetBrushType());
			}
			else if(sCommUI.ShadingEnabled())
				sD3D.ChangeMCCV(0x5, sShader.GetRed(), sShader.GetGreen(), sShader.GetBlue(), sD3D.GetWorldCoords((ui32)sMouse.GetPosition().x, (ui32)sMouse.GetPosition().y), m_radius);
			else if(m_mode == 0)
				sD3D.ChangeHeight(sD3D.GetWorldCoords((ui32)sMouse.GetPosition().x, (ui32)sMouse.GetPosition().y), m_radius, m_intensity, m_type);
			else if(m_mode == 1)
				sD3D.FlattenHeight(sD3D.GetWorldCoords((ui32)sMouse.GetPosition().x, (ui32)sMouse.GetPosition().y), m_radius, powf(0.9f, m_intensity / 20), m_type);
			else if(m_mode == 2)
				sD3D.BlurHeight(sD3D.GetWorldCoords((ui32)sMouse.GetPosition().x, (ui32)sMouse.GetPosition().y), m_radius, m_intensity, m_type);
		}
		else if(state[VK_MENU] & 0x80)
		{
			m_quickKey = true;
			if(sCommUI.TextureEnabled())
			{
				sD3D.ChangeTexture(sD3D.GetWorldCoords((ui32)sMouse.GetPosition().x, (ui32)sMouse.GetPosition().y), 
					m_radius, m_intensity, m_texFlags, m_texUI->GetTexture(), m_texUI->GetBrushType());
			}
			else if(m_mode == 0)
				sD3D.ChangeHeight(sD3D.GetWorldCoords((ui32)sMouse.GetPosition().x, (ui32)sMouse.GetPosition().y), m_radius, -m_intensity, m_type);
			else if(m_mode == 1)
				sD3D.FlattenHeight(sD3D.GetWorldCoords((ui32)sMouse.GetPosition().x, (ui32)sMouse.GetPosition().y), m_radius, powf(0.9f, m_intensity / 20), m_type);
			else if(m_mode == 2)
				sD3D.BlurHeight(sD3D.GetWorldCoords((ui32)sMouse.GetPosition().x, (ui32)sMouse.GetPosition().y), m_radius, m_intensity, m_type);
		
		}
		else if(state[VK_LSHIFT] & 0x80)
		{
			sD3D.DrawShadow(sD3D.GetWorldCoords((ui32)sMouse.GetPosition().x, (ui32)sMouse.GetPosition().y), m_radius, true);
		}
		else if(state[0xE2] & 0x80)
		{
			sD3D.DrawShadow(sD3D.GetWorldCoords((ui32)sMouse.GetPosition().x, (ui32)sMouse.GetPosition().y), m_radius, false);
		}
		else if(m_quickKey)
		{
			sMouse.ResetDownTime(true);
			m_quickKey = false;
		}
		else if(GetTickCount() - sMouse.TimeLeftDown() > 1500 && !ConfigFile::Main().getBoolean("DisableQuickMode"))
		{
			if(!m_quickMode)
			{
				m_quickMode = true;
				sHud.SetText("Entered quickmode!", 2500, 0x00FF0000, 48);
			}
			if(sCommUI.TextureEnabled())
			{
				sD3D.ChangeTexture(sD3D.GetWorldCoords((ui32)sMouse.GetPosition().x, (ui32)sMouse.GetPosition().y), 
					m_radius, m_intensity, m_texFlags, m_texUI->GetTexture(), m_texUI->GetBrushType());
			}
			else if(m_mode == 0)
				sD3D.ChangeHeight(sD3D.GetWorldCoords((ui32)sMouse.GetPosition().x, (ui32)sMouse.GetPosition().y), m_radius, m_intensity, m_type);
			else if(m_mode == 1)
				sD3D.FlattenHeight(sD3D.GetWorldCoords((ui32)sMouse.GetPosition().x, (ui32)sMouse.GetPosition().y), m_radius, powf(0.9f, m_intensity / 20), m_type);
			else if(m_mode == 2)
				sD3D.BlurHeight(sD3D.GetWorldCoords((ui32)sMouse.GetPosition().x, (ui32)sMouse.GetPosition().y), m_radius, m_intensity, m_type);
		}
		else
			m_quickMode = false;

	}

	m_texSel->Update();

	if(m_drawSel)
	{
		float distance = sqrtf(powf(m_position.x - sCamera.GetCamPosition().x, 2.0f) + powf(m_position.y - sCamera.GetCamPosition().y, 2.0f) + powf(m_position.z - sCamera.GetCamPosition().z, 2.0f));
		Vertex3F front(sCamera.GetTargetPosition().x - sCamera.GetCamPosition().x, sCamera.GetTargetPosition().y - sCamera.GetCamPosition().y, sCamera.GetTargetPosition().z - sCamera.GetCamPosition().z);
		Vertex3F target(m_position.x - sCamera.GetCamPosition().x, m_position.y - sCamera.GetCamPosition().y, m_position.z - sCamera.GetCamPosition().z);
		
		float angle = acos((front.x * target.x + front.z * target.z + front.y * target.y) / (front.Length() * target.Length()));

		if((angle * 180 / M_PI < 50))
		{
		
			D3DXMATRIX matWorld, matView, matProj;
			sD3D->GetTransform(D3DTS_PROJECTION, &matProj);
			sD3D->GetTransform(D3DTS_VIEW, &matView);
			D3DXVECTOR3 tmp, out;
			tmp.x = m_position.x;
			tmp.y = m_position.y;
			tmp.z = m_position.z;
			D3DVIEWPORT9 vPort;
			sD3D->GetViewport(&vPort);
			D3DXVec3Project(&out, &tmp, &vPort, &matProj, &matView, &matID);
			sprintf_s<255>(m_coordClient, "Client: X: %.02f  Y: %.02f  Z: %.02f", tmp.x, tmp.y, tmp.z);
			sD3D->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
			ui32 tLen = m_coordinates.GetTextLen(m_coordClient, true);
			ui32 tLenN = m_coordinates.GetTextLen(m_coordClient);
			m_coordinates.DrawTextA(ui32(out.x - (tLen / 2)), ui32(out.y - 30), tLenN, 40, m_coordClient);
		}
	}
	Vertex3F cam = sCamera.GetCamPosition();
	cam.x = ZEROPOINT - cam.x;
	cam.z = ZEROPOINT - cam.z;
	char txt[255];
	sprintf_s<255>(txt, "Server: X: %.02f  Y: %.02f  Z: %.02f", cam.x, cam.z, cam.y);
	m_upBar->SetText(2, txt);
	m_lastMouse = sD3D.GetWorldCoords((ui32)sMouse.GetPosition().x, (ui32)sMouse.GetPosition().y);
	m_layerUI->Update();
	CheckKeys();
	if(AreaDialog::GetInstance())
		sAreaDialog.Update();
	Vertex3F cp = sCamera.GetCamPosition();
	ui32 indexX = (ui32)floor(cp.x / TILESIZE);
	ui32 indexY = (ui32)floor(cp.z / TILESIZE);
	ADTModel* model = sD3D.GetADT(indexX, indexY);
	if(model)
	{
		std::stringstream strm;
		MapEntry* me = dbcMap.LookupEntry(m_mapId);
		strm << "ADT: " << me->mapName << "_" << indexX << "_" << indexY;

		m_upBar->SetTextPos(1, (sD3D.GetSize().x - 50 - m_upBar->GetTextLength(strm.str())));
		m_upBar->SetText(1, strm.str());
		float rX = cp.x - (indexX * TILESIZE);
		float rY = cp.z - (indexY * TILESIZE);
		ui32 chunkX = (ui32)floor(rX / CHUNKSIZE);
		ui32 chunkY = (ui32)floor(rY / CHUNKSIZE);
		ADTChunk* cnk = model->GetChunk(chunkX, chunkY);
		if(cnk)
		{
			AreaTableEntry* e = dbcArea.LookupEntry(cnk->GetAreaId());
			if(e && e->name)
			{
				m_upBar->SetTextPos(0, (sD3D.GetSize().x / 2) - (m_upBar->GetTextLength(e->name)) / 2);
				m_upBar->SetText(0, e->name);
				if(m_lastAreaId != cnk->GetAreaId()) {
					sHud.SetText(e->name, 3500);
					m_lastAreaId = cnk->GetAreaId();
				}
			}
		}
	}
}

void SelectionMgr::CreateWdl(UIButton<SelectionMgr>* /*pButton*/)
{
	sWdl.CreateWdl(dbcMap.LookupEntry(m_mapId)->mapName);
}

void SelectionMgr::ToggleAreaDbc(UIButton<SelectionMgr>*)
{
	sAreaDialog.Toggle();
}

float SelectionMgr::GetTextureGradient()
{
	return m_texUI->GetGradient();
}

float SelectionMgr::GetTextureIntensity()
{
	return m_texUI->GetIntensity();
}

void SelectionMgr::ToggleTextureUI(UIButton<SelectionMgr>*)
{
	m_texUI->Toggle();
}

void SelectionMgr::ToggleTexSelUI(UIButton<SelectionMgr>*)
{
	if(!sTutorial.TutorialShown(TUTORIAL_TEXTURE_SELECTION))
		sTutorial.ShowTutorial("Texture selection", helpTexSel);
	m_texSel->Toggle();
}

void SelectionMgr::ToggleSettings(UIButton<SelectionMgr>*)
{
	sSettings.Toggle();
	sDialog.BringToFront(sSettings);
}

void SelectionMgr::EnableMCCV()
{
	m_texUI->DisableTexturing();
}

void SelectionMgr::EnableTexturing()
{
	sShader.DisableShading();
}

void SelectionMgr::Resize()
{
	m_toolBar->Resize(sD3D.GetSize().y - 58);
	m_upBar->Resize(0);
	sLoading.Resize();
	sChat.Resize();
	sTutorial.Resize();
}

void SelectionMgr::update() {
	m_toolBar->m_visible = sD3D.GetAppState() == STATE_WORLD;
	m_upBar->m_visible = sD3D.GetAppState() == STATE_WORLD;
}